Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl GlobalGraph build hook #246

Merged
merged 4 commits into from
Oct 10, 2024
Merged

Conversation

killagu
Copy link
Contributor

@killagu killagu commented Oct 10, 2024

Checklist
  • npm test passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)
Description of change

Summary by CodeRabbit

  • New Features

    • Added CrossCutGraphHook and PointCutGraphHook to enhance the module's capabilities.
    • Introduced functions for managing cross-cutting concerns and linking pointcut advice within a global graph structure.
  • Bug Fixes

    • Improved test coverage for cross-cutting concerns, ensuring robust functionality.
  • Documentation

    • New package.json files created for hello-cross-cut and hello-point-cut modules to define metadata.
  • Refactor

    • Restructured the Hello class to utilize pointcut advice effectively.
  • Chores

    • Updated utility classes to support new build hooks for enhanced functionality.

Copy link

coderabbitai bot commented Oct 10, 2024

Caution

Review failed

The head commit changed during the review from d25f595 to e740c48.

Walkthrough

The changes in this pull request include modifications to several classes and functions across multiple files. Key updates involve altering method visibility in the AspectMetaBuilder class, adding new exports related to cross-cutting and pointcut hooks in the AOP runtime, and introducing new classes and interfaces for managing call tracing and advice execution. Additionally, the GlobalGraph class has been enhanced with new methods and properties, and test files have been updated to ensure comprehensive coverage of the new functionalities.

Changes

File Path Change Summary
core/aop-decorator/src/AspectMetaBuilder.ts Method visibility updated: private static getAllMethodsstatic getAllMethods
core/aop-runtime/index.ts New exports added: CrossCutGraphHook, PointCutGraphHook
core/aop-runtime/src/CrossCutGraphHook.ts Functions added: crossCutGraphHook, findCrossCuttedClazz, checkClazzMatchCrossCut
core/aop-runtime/src/PointCutGraphHook.ts Functions added: pointCutGraphHook, findPointCutAdvice
core/aop-runtime/test/aop-runtime.test.ts Enhanced tests with new imports and test cases for cross-cutting concerns
core/aop-runtime/test/fixtures/modules/hello_cross_cut/CallTrace.ts New class CallTrace and interface CallTraceMsg added
core/aop-runtime/test/fixtures/modules/hello_cross_cut/HelloCrossCut.ts New class CrosscutAdvice implementing IAdvice interface added
core/aop-runtime/test/fixtures/modules/hello_cross_cut/package.json New package.json file created for hello-cross-cut module
core/aop-runtime/test/fixtures/modules/hello_point_cut/HellloPointCut.ts New class PointcutAdvice implementing IAdvice interface added
core/aop-runtime/test/fixtures/modules/hello_point_cut/package.json New package.json file created for hello-point-cut module
core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts Significant modifications to Hello class, removing previous advice and adding new methods
core/metadata/src/model/graph/GlobalGraph.ts Enhancements to GlobalGraph class with new methods and property access level modifications
core/test-util/CoreTestHelper.ts Updated prepareModules method to accept build hooks
core/test-util/LoaderUtil.ts Updated buildGlobalGraph method to accept build hooks

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant GlobalGraph
    participant CrossCutGraphHook
    participant PointCutGraphHook

    User->>GlobalGraph: Register build hooks
    GlobalGraph->>CrossCutGraphHook: Add cross-cutting concerns
    GlobalGraph->>PointCutGraphHook: Link pointcut advice
Loading

🐰 "In the meadow where changes bloom,
New hooks and classes dispel the gloom.
With methods shining bright and clear,
AOP magic draws us near!
Let's hop with joy, our code refined,
In this garden of logic, happiness we find!" 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Outside diff range and nitpick comments (16)
core/aop-runtime/test/fixtures/modules/hello_cross_cut/CallTrace.ts (3)

3-10: LGTM: Well-structured interface with a minor improvement suggestion.

The CallTraceMsg interface provides a clear contract for call trace messages. However, consider using a more specific type for adviceParams instead of any to improve type safety.

You could replace any with a more specific type or use a generic type parameter:

export interface CallTraceMsg<T = unknown> {
  // ... other properties
  adviceParams?: T;
}

This allows users of the interface to specify the type of adviceParams when needed, while still maintaining flexibility.


12-21: LGTM: Well-implemented singleton class with potential for enhancement.

The CallTrace class is correctly implemented as a singleton with public access, which is suitable for a global call trace collector. The addMsg method serves its purpose well.

Consider adding the following methods to enhance functionality:

  1. A method to retrieve messages:
getMessages(): Array<CallTraceMsg> {
  return [...this.msgs]; // Return a copy to prevent external modifications
}
  1. A method to clear messages:
clearMessages(): void {
  this.msgs = [];
}

These additions would provide more control over the collected messages and could be useful for testing or debugging purposes.


1-21: Enhance file documentation for clarity and usage guidance.

While the implementation is solid, the file could benefit from additional documentation to explain its purpose and usage, especially considering its location in the test fixtures directory.

Consider adding a file-level JSDoc comment at the beginning of the file:

/**
 * This file defines structures and a singleton class for collecting call trace messages.
 * It is primarily used for testing and debugging Aspect-Oriented Programming (AOP) functionality.
 * 
 * @module CallTrace
 */

This documentation would help other developers understand the purpose and context of this code more quickly.

core/test-util/CoreTestHelper.ts (2)

8-14: LGTM with a minor suggestion.

The new imports are correctly added and necessary for the changes in the prepareModules method. However, to maintain consistency with the project's coding style and address the static analysis hint, please add a trailing comma after the last import.

Apply this small change:

 import {
   EggLoadUnitType,
   EggPrototype,
   GlobalGraph,
   GlobalGraphBuildHook,
-  LoadUnitFactory
+  LoadUnitFactory,
 } from '@eggjs/tegg-metadata';
🧰 Tools
🪛 GitHub Check: Runner-ubuntu (22)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (20)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (18)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (20)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (18)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (16)

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (16)

[failure] 13-13:
Missing trailing comma


41-47: LGTM with suggestions for improved robustness and clarity.

The changes to the prepareModules method look good overall. The addition of the hooks parameter provides more flexibility in the graph building process. However, I have two suggestions to improve the code:

  1. Add a null check for GlobalGraph.instance to prevent potential runtime errors:
-    for (const { path } of GlobalGraph.instance!.moduleConfigList) {
+    if (!GlobalGraph.instance) {
+      throw new Error('GlobalGraph instance is not initialized');
+    }
+    for (const { path } of GlobalGraph.instance.moduleConfigList) {
  1. Add a comment explaining the relationship between moduleDirs and GlobalGraph.instance!.moduleConfigList to improve code clarity:
// Note: GlobalGraph.instance!.moduleConfigList is populated by LoaderUtil.buildGlobalGraph
// and should contain the same paths as moduleDirs
for (const { path } of GlobalGraph.instance!.moduleConfigList) {
  // ...
}

These changes will make the code more robust and easier to understand for future maintainers.

core/aop-decorator/src/AspectMetaBuilder.ts (2)

Line range hint 29-46: Consider optimizing and improving type safety of getAllMethods

While the current implementation of getAllMethods is functional, consider the following improvements:

  1. Add type annotations for better type safety:
static getAllMethods(clazz: EggProtoImplClass): PropertyKey[] {
  const methodSet = new Set<PropertyKey>();
  // ... rest of the implementation
}
  1. Move the inner getMethods function outside to avoid recreation on each call:
private static getMethods(obj: any, methodSet: Set<PropertyKey>): void {
  if (obj) {
    const propDescs = Object.getOwnPropertyDescriptors(obj);
    for (const [name, desc] of Object.entries(propDescs)) {
      if (desc.value instanceof Function) {
        methodSet.add(name);
      }
    }
    this.getMethods(Object.getPrototypeOf(obj), methodSet);
  }
}

static getAllMethods(clazz: EggProtoImplClass): PropertyKey[] {
  const methodSet = new Set<PropertyKey>();
  this.getMethods(clazz.prototype, methodSet);
  return Array.from(methodSet);
}
  1. Consider using a loop instead of recursion for better performance with deep prototype chains:
static getAllMethods(clazz: EggProtoImplClass): PropertyKey[] {
  const methodSet = new Set<PropertyKey>();
  let obj = clazz.prototype;
  while (obj) {
    const propDescs = Object.getOwnPropertyDescriptors(obj);
    for (const [name, desc] of Object.entries(propDescs)) {
      if (desc.value instanceof Function) {
        methodSet.add(name);
      }
    }
    obj = Object.getPrototypeOf(obj);
  }
  return Array.from(methodSet);
}

These changes could improve type safety, reduce memory usage, and potentially improve performance for classes with deep prototype chains.


Line range hint 1-58: Enhance documentation and consider refactoring AspectMetaBuilder

The AspectMetaBuilder class is well-structured, but consider the following improvements:

  1. Add comprehensive class-level documentation explaining the purpose and usage of AspectMetaBuilder.

  2. Consider refactoring doBuildMethodAspect for better readability:

private doBuildMethodAspect(method: PropertyKey): Aspect | undefined {
  const crosscutAdviceList = this.getCrosscutAdvice(method);
  const pointcutAdviceList = this.getPointcutAdvice(method);
  
  if (!this.hasAdvice(crosscutAdviceList, pointcutAdviceList)) return;
  
  return this.buildAspect(method, crosscutAdviceList, pointcutAdviceList);
}

private getCrosscutAdvice(method: PropertyKey) {
  return this.crosscutAdviceFactory.getAdvice(this.clazz, method);
}

private getPointcutAdvice(method: PropertyKey) {
  return PointcutAdviceInfoUtil.getPointcutAdviceInfoList(this.clazz, method);
}

private hasAdvice(crosscutAdviceList: any[], pointcutAdviceList: any[]): boolean {
  return crosscutAdviceList.length > 0 || pointcutAdviceList.length > 0;
}

private buildAspect(method: PropertyKey, crosscutAdviceList: any[], pointcutAdviceList: any[]): Aspect {
  const aspectBuilder = new AspectBuilder(this.clazz, method);
  [...crosscutAdviceList, ...pointcutAdviceList].forEach(advice => aspectBuilder.addAdvice(advice));
  return aspectBuilder.build();
}
  1. Improve error handling, especially for edge cases. For example, add checks for null or undefined values in the constructor and methods.

  2. Consider adding logging for important operations or potential issues.

These changes would improve the maintainability and robustness of the AspectMetaBuilder class.

core/test-util/LoaderUtil.ts (3)

37-37: LGTM: Method signature updated correctly with a minor suggestion.

The buildGlobalGraph method signature has been appropriately updated to include the optional hooks parameter. This change enhances the flexibility of the graph building process.

Consider using a default value for the hooks parameter to simplify the method body:

static buildGlobalGraph(modulePaths: string[], hooks: GlobalGraphBuildHook[] = [])

This would eliminate the need for the null coalescing operator in the for loop.


39-41: LGTM: Hook registration loop implemented correctly with a minor suggestion.

The new loop correctly iterates over the hooks array and registers each hook with the GlobalGraph instance. This implementation aligns with the intended functionality of enabling the registration of build hooks during the graph construction process.

To improve readability and reduce nesting, consider using the Array.prototype.forEach method:

(hooks ?? []).forEach(hook => GlobalGraph.instance.registerBuildHook(hook));

This change would make the code more concise and easier to read.


Test coverage for buildGlobalGraph and GlobalGraphBuildHook is missing.

The recent changes to the LoaderUtil class introduce new functionalities related to buildGlobalGraph and GlobalGraphBuildHook. However, our verification indicates that there are no existing test cases covering these methods.

  • Missing Tests:
    • buildGlobalGraph method with hooks
    • GlobalGraphBuildHook functionality

Please add unit tests to ensure these new features are properly tested and to maintain code reliability.

🔗 Analysis chain

Line range hint 2-41: Overall implementation looks good with a suggestion for test coverage.

The changes to the LoaderUtil class, including the updated import statement, method signature, and new hook registration loop, are well-implemented and consistent with the intended functionality. The modifications enhance the flexibility of the graph building process by allowing the registration of build hooks.

To ensure the new functionality is properly tested, please run the following script to check for test coverage of the buildGlobalGraph method:

If the script doesn't return any results, consider adding test cases to cover the new functionality with hooks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for the buildGlobalGraph method

# Test: Search for test cases that use the buildGlobalGraph method with hooks
rg --type typescript -g '*.test.ts' -g '*.spec.ts' 'buildGlobalGraph.*hooks'

# Test: Search for test cases that specifically test the hook functionality
rg --type typescript -g '*.test.ts' -g '*.spec.ts' 'GlobalGraphBuildHook'

Length of output: 222


Script:

#!/bin/bash
# Description: Verify test coverage for the buildGlobalGraph method

# Test: Search for test cases that use the buildGlobalGraph method with hooks
rg -g '*.test.ts' -g '*.spec.ts' 'buildGlobalGraph.*hooks'

# Test: Search for test cases that specifically test the hook functionality
rg -g '*.test.ts' -g '*.spec.ts' 'GlobalGraphBuildHook'

Length of output: 116


Script:

#!/bin/bash
# Description: Broadly verify test coverage for the buildGlobalGraph method and GlobalGraphBuildHook functionality

# Test: Search all TypeScript test files for usage of buildGlobalGraph method with hooks
rg -g '*.test.ts' -g '*.spec.ts' 'buildGlobalGraph\s*\(.*hooks'

# Test: Search all TypeScript test files for usage of GlobalGraphBuildHook
rg -g '*.test.ts' -g '*.spec.ts' 'GlobalGraphBuildHook'

# Test: Search any TypeScript files for buildGlobalGraph method implementations
rg -g '*.ts' 'buildGlobalGraph'

# Test: Search any TypeScript files for GlobalGraphBuildHook implementations
rg -g '*.ts' 'GlobalGraphBuildHook'

Length of output: 2480

core/aop-runtime/src/PointCutGraphHook.ts (1)

39-39: Consider handling missing property more gracefully

While the assert statement in line 39 ensures that property exists, relying on assertions might not be ideal in a production environment. Consider handling the case where property is missing with a proper error message or exception to provide clearer feedback.

For example:

  const property = PrototypeUtil.getProperty(clazz);
- assert(property, 'not found property');
+ if (!property) {
+   throw new Error(`Property not found for class ${clazz.name}`);
+ }

This provides a more informative error message and aligns with standard error-handling practices.

core/aop-runtime/test/fixtures/modules/hello_cross_cut/HelloCrossCut.ts (2)

37-37: Narrow the type of 'result' parameter

Using any for the result parameter reduces type safety. If possible, specify a more precise type based on what the hello method returns.

For example:

-async afterReturn(ctx: AdviceContext<Hello, AfterReturnParams>, result: any): Promise<void> {
+async afterReturn(ctx: AdviceContext<Hello, AfterReturnParams>, result: string): Promise<void> {

65-65: Avoid mutating 'ctx.args' directly

Directly modifying ctx.args can lead to unexpected side effects, especially if other advices rely on the original arguments. Consider creating a shallow copy of the arguments before modifying them.

Refactor the code as follows:

-ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`;
+const newArgs = [...ctx.args];
+newArgs[0] = `withCrosscutAroundParam(${ctx.args[0]})`;
+ctx.args = newArgs;
core/aop-runtime/src/CrossCutGraphHook.ts (1)

10-26: Consider Performance Implications of Nested Loops

In the crossCutGraphHook function, there are nested loops over the moduleGraph and protos, which might lead to performance issues if the graphs are large. Consider optimizing the iteration or adding conditions to reduce unnecessary processing.

One possible optimization is to filter the modules or protos before iterating:

 for (const moduleNode of globalGraph.moduleGraph.nodes.values()) {
+  if (!moduleNode.val.hasCrossCutProtos) continue;
   for (const crossCutProtoNode of moduleNode.val.protos) {

Ensure that any added properties like hasCrossCutProtos are properly maintained.

core/aop-runtime/test/fixtures/modules/hello_point_cut/HellloPointCut.ts (1)

13-13: Consider translating the comment to English for consistency

The comment on line 13 is in Chinese. For consistency and maintainability across the codebase, it's preferable to use English for code comments.

core/metadata/src/model/graph/GlobalGraph.ts (1)

75-76: Add documentation for registerBuildHook method

The registerBuildHook method allows registration of build hooks. Adding JSDoc comments or documentation would improve maintainability and help other developers understand its usage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f01fb63 and 38f0950.

📒 Files selected for processing (14)
  • core/aop-decorator/src/AspectMetaBuilder.ts (1 hunks)
  • core/aop-runtime/index.ts (1 hunks)
  • core/aop-runtime/src/CrossCutGraphHook.ts (1 hunks)
  • core/aop-runtime/src/PointCutGraphHook.ts (1 hunks)
  • core/aop-runtime/test/aop-runtime.test.ts (2 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_cross_cut/CallTrace.ts (1 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_cross_cut/HelloCrossCut.ts (1 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_cross_cut/package.json (1 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_point_cut/HellloPointCut.ts (1 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_point_cut/package.json (1 hunks)
  • core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts (1 hunks)
  • core/metadata/src/model/graph/GlobalGraph.ts (8 hunks)
  • core/test-util/CoreTestHelper.ts (2 hunks)
  • core/test-util/LoaderUtil.ts (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • core/aop-runtime/test/fixtures/modules/hello_cross_cut/package.json
  • core/aop-runtime/test/fixtures/modules/hello_point_cut/package.json
🧰 Additional context used
🪛 Biome
core/aop-runtime/test/fixtures/modules/hello_cross_cut/HelloCrossCut.ts

[error] 21-21: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🪛 GitHub Check: Runner-ubuntu (22)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (20)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (18)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (20)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (18)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-macos (16)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🪛 GitHub Check: Runner-ubuntu (16)
core/test-util/CoreTestHelper.ts

[failure] 13-13:
Missing trailing comma

🔇 Additional comments (15)
core/aop-runtime/index.ts (1)

5-6: New exports added for CrossCutGraphHook and PointCutGraphHook

The changes look good. Two new exports have been added to the index file, expanding the module's public API with additional hooks related to aspect-oriented programming.

To ensure the correctness of these new exports, let's verify the existence of the source files:

This script will help confirm that the source files for the new exports exist in the expected location.

✅ Verification successful

Verification Successful: All Newly Exported Modules Exist

The newly added exports for CrossCutGraphHook and PointCutGraphHook have been verified. Both source files exist in the expected locations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify the existence of the newly exported modules

echo "Checking for CrossCutGraphHook.ts:"
fd --type f --full-path "core/aop-runtime/src/CrossCutGraphHook.ts"

echo "Checking for PointCutGraphHook.ts:"
fd --type f --full-path "core/aop-runtime/src/PointCutGraphHook.ts"

Length of output: 377

core/aop-runtime/test/fixtures/modules/hello_cross_cut/CallTrace.ts (1)

1-1: LGTM: Import statement is correct and necessary.

The import statement correctly brings in the required decorators from the "@eggjs/core-decorator" package, which are used later in the file.

core/aop-decorator/src/AspectMetaBuilder.ts (2)

Line range hint 1-58: Summary of review and next steps

This review has covered several aspects of the AspectMetaBuilder class:

  1. The change in visibility of getAllMethods from private to public.
  2. Potential optimizations and type safety improvements for getAllMethods.
  3. Suggestions for overall class structure, documentation, and error handling.

Next steps:

  1. Address the visibility change of getAllMethods, ensuring it's intentional and secure.
  2. Consider implementing the suggested optimizations for getAllMethods.
  3. Enhance the class documentation and consider refactoring doBuildMethodAspect.
  4. Improve error handling throughout the class.

Please review these suggestions and implement those that align with the project's goals and coding standards. If you have any questions or need further clarification on any point, please don't hesitate to ask.


29-29: Consider the implications of changing method visibility

The visibility of getAllMethods has been changed from private static to static, making it publicly accessible. While this allows for broader use of the method, it may have implications for encapsulation and security. Please ensure that:

  1. This change is intentional and necessary.
  2. It doesn't expose any sensitive information or operations.
  3. The method's documentation is updated to reflect its new public status.
  4. Any potential misuse of this method is considered and mitigated.

To check the impact of this change, let's verify its usage across the codebase:

This will help us understand if the method is being used outside of its original context, which might justify the visibility change.

✅ Verification successful

Visibility Change of getAllMethods Confirmed

The method getAllMethods has been updated to static and is utilized in the following files:

  • core/aop-runtime/src/CrossCutGraphHook.ts
  • core/aop-runtime/src/PointCutGraphHook.ts
  • core/aop-runtime/src/LoadUnitAopHook.ts
  • core/aop-decorator/test/AspectMetaBuilder.test.ts

This change appears intentional for broader internal usage. Please ensure that:

  1. The method does not expose any sensitive information or operations.
  2. Documentation is updated to reflect its new public status.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of AspectMetaBuilder.getAllMethods

# Search for direct calls to AspectMetaBuilder.getAllMethods
echo "Direct calls to AspectMetaBuilder.getAllMethods:"
rg --type typescript "AspectMetaBuilder\.getAllMethods"

# Search for potential imports of AspectMetaBuilder
echo "\nPotential imports of AspectMetaBuilder:"
rg --type typescript "import.*AspectMetaBuilder"

Length of output: 378


Script:

#!/bin/bash
# Description: Check usage of AspectMetaBuilder.getAllMethods

# Search for direct calls to AspectMetaBuilder.getAllMethods in .ts and .tsx files
echo "Direct calls to AspectMetaBuilder.getAllMethods:"
rg "AspectMetaBuilder\.getAllMethods" --glob "*.ts" --glob "*.tsx"

# Search for potential imports of AspectMetaBuilder in .ts and .tsx files
echo "\nPotential imports of AspectMetaBuilder:"
rg "import.*AspectMetaBuilder" --glob "*.ts" --glob "*.tsx"

Length of output: 1162

core/test-util/LoaderUtil.ts (1)

2-2: LGTM: Import statement updated correctly.

The import statement has been appropriately updated to include GlobalGraphBuildHook, which is necessary for the new functionality in the buildGlobalGraph method.

core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts (1)

Line range hint 5-14: The Hello class and its methods are correctly implemented

The Hello class and its methods hello and helloWithException are well-defined with appropriate decorators. The use of @Pointcut and the integration with PointcutAdvice and pointcutAdviceParams is consistent and follows best practices.

core/aop-runtime/src/PointCutGraphHook.ts (1)

12-26: Implementation of pointCutGraphHook looks good

The pointCutGraphHook function correctly iterates over module nodes and associates pointcut advice prototypes. The logic is well-structured and adheres to best practices.

core/aop-runtime/src/CrossCutGraphHook.ts (1)

56-62: Verify Method Matching Logic in checkClazzMatchCrossCut

In the checkClazzMatchCrossCut function, ensure that the method matching logic correctly identifies matching methods. Confirm that AspectMetaBuilder.getAllMethods(proto.clazz) retrieves all relevant methods and that crosscutInfo.pointcutInfo.match(proto.clazz, method) accurately determines a match.

Run the following script to list all methods and verify the matching:

This script uses ast-grep to parse class methods and jq to format the output for inspection.

core/aop-runtime/test/fixtures/modules/hello_point_cut/HellloPointCut.ts (1)

19-82: Well-structured implementation of PointcutAdvice class

The PointcutAdvice class provides a comprehensive AOP implementation with correctly overridden methods for beforeCall, afterReturn, afterThrow, afterFinally, and around. The use of assertions and logging via callTrace enhances robustness and debuggability.

core/aop-runtime/test/aop-runtime.test.ts (3)

15-17: New hooks and utilities are correctly imported

The imports for crossCutGraphHook, pointCutGraphHook, and CallTrace are properly added, enhancing the test suite with necessary hooks and tracing capabilities.


39-41: Additional modules are properly included in test setup

The modules for hello_point_cut and hello_cross_cut are correctly included in the test preparation, ensuring that pointcut and crosscut functionalities are tested.


42-43: Hooks are correctly registered in the test lifecycle

The addition of crossCutGraphHook and pointCutGraphHook to the hooks array ensures that the AOP graph hooks are applied during the test execution.

core/metadata/src/model/graph/GlobalGraph.ts (3)

22-23: Definition of GlobalGraphBuildHook is appropriate

The new type GlobalGraphBuildHook is correctly defined and enhances the extensibility of the GlobalGraph class by allowing external hooks during the build process.


94-94: Refactored code enhances readability

The extraction of buildInjectEdge from the build method improves code organization and readability, separating concerns and making the build method cleaner.


160-160: Assess necessity of making findDependencyProtoNode public

The method #findDependencyProtoNode has been changed from private to public, potentially altering the class's encapsulation. Ensure that this method needs to be public. If it's intended to be accessed externally, confirm that it is used outside this class, and consider adding documentation.

Run the following script to verify if findDependencyProtoNode is called outside of GlobalGraph:

✅ Verification successful

findDependencyProtoNode is utilized outside of GlobalGraph, validating its public visibility.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for external usage of findDependencyProtoNode

# This script searches for 'findDependencyProtoNode' in .ts files excluding GlobalGraph.ts
rg --type ts --glob '!core/metadata/src/model/graph/GlobalGraph.ts' 'findDependencyProtoNode'

Length of output: 220

Comment on lines +15 to +16
const pointCutAdviceProtoList = findPointCutAdvice(globalGraph, pointCuttedProtoNode);
if (!pointCutAdviceProtoList) continue;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify condition by checking array length

With findPointCutAdvice modified to always return an array, you can simplify the condition in line 15 by checking if the array is empty.

Apply this diff:

-      if (!pointCutAdviceProtoList) continue;
+      if (pointCutAdviceProtoList.length === 0) continue;

This makes the code more readable and avoids potential issues with falsy values other than undefined.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pointCutAdviceProtoList = findPointCutAdvice(globalGraph, pointCuttedProtoNode);
if (!pointCutAdviceProtoList) continue;
const pointCutAdviceProtoList = findPointCutAdvice(globalGraph, pointCuttedProtoNode);
if (pointCutAdviceProtoList.length === 0) continue;

Comment on lines +44 to +45
...property?.qualifiers ?? [],
...QualifierUtil.getProtoQualifiers(clazz),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify qualifiers array construction

In lines 44-45, to handle the possibility that property.qualifiers is undefined, you can default it to an empty array when spreading. This removes the need for optional chaining and ensures the spread operator functions correctly.

Apply this diff:

           qualifiers: [
-              ...property?.qualifiers ?? [],
+              ...(property.qualifiers ?? []),
               ...QualifierUtil.getProtoQualifiers(clazz),
           ],

This change simplifies the code and improves readability by making the handling of potential undefined values explicit.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
...property?.qualifiers ?? [],
...QualifierUtil.getProtoQualifiers(clazz),
...(property.qualifiers ?? []),
...QualifierUtil.getProtoQualifiers(clazz),

function findPointCutAdvice(globalGraph: GlobalGraph, protoNode: GraphNode<ProtoNode, ProtoDependencyMeta>) {
const proto = protoNode.val.proto;
if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) {
return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure consistent return types in findPointCutAdvice

Currently, findPointCutAdvice returns undefined if the prototype is not a class prototype descriptor. For consistency and to simplify downstream code, consider returning an empty array instead. This allows you to avoid undefined checks when iterating over the results.

Apply this diff:

  if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) {
-    return;
+    return [];
  }

This ensures that the function always returns an array, making it easier to handle in the calling code.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return;
return [];

@Inject()
callTrace: CallTrace;

async beforeCall(ctx: AdviceContext<Hello, {}>): Promise<void> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using '{}' as a type; specify an explicit interface

Using {} as a type is discouraged because it represents any non-nullable value, which can lead to unclear code and potential type safety issues. Instead, define a specific interface or type that accurately represents the expected structure.

Consider defining an explicit type for the context parameters:

interface BeforeCallParams {
  // Define the expected properties here
}

async beforeCall(ctx: AdviceContext<Hello, BeforeCallParams>): Promise<void> {
🧰 Tools
🪛 Biome

[error] 25-25: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

import { CallTrace, Hello, crosscutAdviceParams, pointcutAdviceParams } from './fixtures/modules/hello_succeed/Hello';
import { Hello } from './fixtures/modules/hello_succeed/Hello';
import { crosscutAdviceParams } from './fixtures/modules/hello_cross_cut/HelloCrossCut';
import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HellloPointCut';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the typo in the import path

There's a typo in the import statement on line 11. The file name should likely be HelloPointCut instead of HellloPointCut.

Apply this diff to fix the import path:

-import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HellloPointCut';
+import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HelloPointCut';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HellloPointCut';
import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HelloPointCut';

@@ -38,13 +40,13 @@ export class GlobalGraph {
* Edge: ModuleDependencyMeta, prototype and it's inject object
* @private
*/
private moduleGraph: Graph<GlobalModuleNode, ModuleDependencyMeta>;
moduleGraph: Graph<GlobalModuleNode, ModuleDependencyMeta>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Re-evaluate changing access modifiers from private to public

The properties moduleGraph and protoGraph have their access modifiers changed from private to public. Exposing these internal graphs may compromise encapsulation and allow external code to modify internal state. Consider if it is necessary to expose these properties, or if providing controlled access via getter methods would be safer.

Also applies to: 49-49

Comment on lines +98 to +100
for (const buildHook of this.buildHooks) {
buildHook(this);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling when executing build hooks

When iterating over buildHooks, if a hook throws an error, it may halt the build process and prevent subsequent hooks from executing. Consider adding error handling to ensure robustness.

Apply this diff to add error handling:

 for (const buildHook of this.buildHooks) {
+  try {
     buildHook(this);
+  } catch (error) {
+    // Log the error and continue
+    console.error('Error executing build hook:', error);
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const buildHook of this.buildHooks) {
buildHook(this);
}
for (const buildHook of this.buildHooks) {
try {
buildHook(this);
} catch (error) {
// Log the error and continue
console.error('Error executing build hook:', error);
}
}

Comment on lines +124 to +129
if (!injectModule) {
if (!this.strict) {
return;
}
throw new Error(`not found module ${injectNode.val.proto.instanceModuleName}`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistent error handling and messaging

In the addInject method, when injectModule is not found in strict mode, a generic Error is thrown. In contrast, other parts of the code use FrameworkErrorFormater.formatError with specific error types. For consistency, consider using a specific error type and consistent formatting.

Apply this diff to align error handling:

     if (!injectModule) {
       if (!this.strict) {
         return;
       }
-      throw new Error(`not found module ${injectNode.val.proto.instanceModuleName}`);
+      throw FrameworkErrorFormater.formatError(new EggPrototypeNotFound(injectNode.val.proto.instanceModuleName, moduleNode.val.name));
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!injectModule) {
if (!this.strict) {
return;
}
throw new Error(`not found module ${injectNode.val.proto.instanceModuleName}`);
}
if (!injectModule) {
if (!this.strict) {
return;
}
throw FrameworkErrorFormater.formatError(new EggPrototypeNotFound(injectNode.val.proto.instanceModuleName, moduleNode.val.name));
}

Comment on lines +204 to 207
findModuleNode(moduleName: string) {
for (const node of this.moduleGraph.nodes.values()) {
if (node.val.name === moduleName) {
return node;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Optimize module lookup in findModuleNode

The findModuleNode method performs a linear search over moduleGraph.nodes.values(). If module lookups are frequent or the number of modules is large, consider using a Map to store modules by name for efficient lookups.

You could maintain a Map<string, GraphNode<GlobalModuleNode, ModuleDependencyMeta>> for quick access:

 // Add a new private property to store modules by name
+private moduleNodeMap: Map<string, GraphNode<GlobalModuleNode, ModuleDependencyMeta>> = new Map();

 // Update `addModuleNode` to populate the map
 addModuleNode(moduleNode: GlobalModuleNode) {
   const graphNode = new GraphNode<GlobalModuleNode, ModuleDependencyMeta>(moduleNode);
   if (!this.moduleGraph.addVertex(graphNode)) {
     throw new Error(`duplicate module: ${moduleNode}`);
   }
   this.moduleNodeMap.set(moduleNode.name, graphNode);
   // ...
 }

 // Refactor `findModuleNode` to use the map
 findModuleNode(moduleName: string) {
-  for (const node of this.moduleGraph.nodes.values()) {
-    if (node.val.name === moduleName) {
-      return node;
-    }
-  }
+  return this.moduleNodeMap.get(moduleName);
 }

Committable suggestion was skipped due to low confidence.

@killagu killagu merged commit 48fce45 into master Oct 10, 2024
12 checks passed
@killagu killagu deleted the feat/global_graph_build_hook branch October 10, 2024 04:12
@coderabbitai coderabbitai bot mentioned this pull request Nov 5, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant